﻿using System;
using StyleCop;
using StyleCop.CSharp;

namespace Jsl.StyleCop.CSharp
{
    /// <summary>
    /// Helper utilities for Rule Analyzers.
    /// </summary>
    /// <remarks>
    /// I started out using a base class but StyleCop blew up when if found an abstract class derived off of SourceAnalyzer.
    /// The class holds the common code that would have been in base class.
    /// </remarks>
    public static class RuleAnalyzerHelper
    {
        #region Public Methods
        /// <summary>
        /// Analyzes the document rules.
        /// </summary>
        /// <param name="document">The document to analyze.</param>
        /// <param name="rules">The rules to run.</param>
        public static void AnalyzeDocumentRules(CodeDocument document, params CsRuleBase<SourceAnalyzer>[] rules)
        {
            if (rules == null)
            {
                throw new ArgumentNullException("rules");
            }

            var csharpDocument = (CsDocument)document;

            // Analyze each document rule.
            foreach (var rule in rules)
            {
                rule.Document = csharpDocument;
                rule.AnalyzeDocument();
            }
        }

        /// <summary>
        /// Analyzes the graph rules.
        /// </summary>
        /// <param name="document">The document to analyze.</param>
        /// <param name="rules">The rules to run.</param>
        public static void AnalyzeGraphRules(CodeDocument document, params CsRuleBase<SourceAnalyzer>[] rules)
        {
            if (rules == null)
            {
                throw new ArgumentNullException("rules");
            }

            var csharpDocument = (CsDocument)document;

            // Initialize each of the rules.
            foreach (var rule in rules)
            {
                rule.Document = csharpDocument;
            }

            // Run each graph rule.
            if (rules.Length > 0)
            {
                csharpDocument.WalkDocument(AnalyzeElement, rules);
            }
        }
        #endregion Public Methods

        #region Private Methods
        private static bool AnalyzeElement(CsElement element, CsElement parentElement, CsRuleBase<SourceAnalyzer>[] rules)
        {
            switch (element.ElementType)
            {
                case ElementType.Accessor:                  // Property get/set, event add/remove and indexers.
                case ElementType.Constructor:
                case ElementType.ConstructorInitializer:
                case ElementType.Destructor:
                case ElementType.Method:
                    RuleAnalyzerHelper.RunRules(element, rules);
                    break;
            }

            return true;
        }

        private static void RunRules(CsElement element, CsRuleBase<SourceAnalyzer>[] rules)
        {
            CodeUnitGraph graph = null;
            foreach (var rule in rules)
            {
                // Check to make sure this rule cares about the element so we don't have to create a graph if we don't need to.
                if (rule.ShouldAnalyzeElementGraph(element))
                {
                    // Only create the graph if it is needed.
                    if (graph == null)
                    {
                        graph = FlowGraph.BuildFlowGraph(element);
                    }

                    rule.AnalyzeGraph(element, graph);
                }
            }
        }
        #endregion Private Methods
    }
}